package com.heima.article.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.ApArticleConfigMapper;
import com.heima.article.mapper.ApArticleContentMapper;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.mapper.ApAuthorMapper;
import com.heima.article.service.ApArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustException;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.ArticleHomeDto;
import com.heima.model.article.pojos.*;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ApArticleServiceImpl extends ServiceImpl<ApArticleMapper, ApArticle> implements ApArticleService {
    @Autowired
    ApAuthorMapper apAuthorMapper;
    @Autowired
    ApArticleConfigMapper apArticleConfigMapper;
    @Autowired
    ApArticleContentMapper apArticleContentMapper;
    @Autowired
    ApArticleMapper apArticleMapper;
    @Value("${file.oss.web-site}")
    private String webSite;
    @Autowired
    KafkaTemplate kafkaTemplate;
    @Autowired
    StringRedisTemplate redisTemplate;

    /**
     * 保存app文章
     *
     * @param dto
     * @return
     */
    @Override
    public ResponseResult saveArticle(ArticleDto dto) {
        //参数校验
        if (dto == null || StringUtils.isBlank(dto.getContent())) {
            CustException.cust("文章参数非法");
        }
        //参数拷贝
        ApArticle apArticle = new ApArticle();
        BeanUtils.copyProperties(dto, apArticle);
        //补全作者id(先查作者)
        String authorName = dto.getAuthorName();
        if (StringUtils.isNotBlank(authorName)) {
            ApAuthor apAuthor = apAuthorMapper.selectOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getName, authorName));
            if (apAuthor != null) {
                apArticle.setAuthorId(apAuthor.getId());
            }
        }
        //判断文章id是否存在,不存在新增,存在修改(操作ap_article表)
        if (apArticle.getId() == null) {
            //新增ap_article
            save(apArticle);
        } else {
            //更新
            ApArticle article = getById(apArticle.getId());
            if (article == null) {
                CustException.cust("文章数据已删除");
            }
            //更新前,先根据文章的id删除ArticleContent和ArticleConfig表数据
            apArticleConfigMapper.delete(Wrappers.<ApArticleConfig>lambdaQuery()
                    .eq(ApArticleConfig::getArticleId, apArticle.getId()));
            apArticleContentMapper.delete(Wrappers.<ApArticleContent>lambdaQuery()
                    .eq(ApArticleContent::getArticleId, apArticle.getId()));

            updateById(apArticle);
        }
        //新增ap_article_config表数据
        ApArticleConfig config = new ApArticleConfig();
        config.setArticleId(apArticle.getId());
        config.setIsComment(true);
        config.setIsForward(true);
        config.setIsDown(false);
        config.setIsDelete(false);
        apArticleConfigMapper.insert(config);
        //新增ap_article_content
        ApArticleContent content = new ApArticleContent();
        content.setArticleId(apArticle.getId());
        content.setContent(dto.getContent());
        apArticleContentMapper.insert(content);

        //*****同步ES索引库********
        Map<String, Object> map = new HashMap<>();
        map.put("id", apArticle.getId().toString());
        map.put("publishTime", apArticle.getPublishTime());
        map.put("layout", apArticle.getLayout());
        map.put("images", apArticle.getImages());
        map.put("authorId", apArticle.getAuthorId());
        map.put("title", apArticle.getTitle());
        kafkaTemplate.send(ArticleConstants.ARTICL_ES_SYNC_TOPIC, JSON.toJSONString(map));

        //返回文章id
        return ResponseResult.okResult(apArticle.getId());
    }

    /**
     * app端展示页面/获取最新/获取更多
     *
     * @param loadType
     * @param dto
     * @return
     */
    @Override
    public ResponseResult load(Short loadType, ArticleHomeDto dto) {
        //参数校验
        if (loadType == null || dto == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //页面
        Integer size = dto.getSize();
        if (size == null || size <= 0) {
            size = 10;
            dto.setSize(size);
        }
        //时间
        if (dto.getMaxBehotTime() == null) {
            dto.setMaxBehotTime(new Date());
        }
        if (dto.getMinBehotTime() == null) {
            dto.setMinBehotTime(new Date());
        }
        //类型
        if (!loadType.equals(ArticleConstants.LOADTYPE_LOAD_MORE) && !loadType.equals(ArticleConstants.LOADTYPE_LOAD_NEW)) {
            loadType = ArticleConstants.LOADTYPE_LOAD_MORE;
        }
        //执行查询
        List<ApArticle> articleList = apArticleMapper.loadArticleList(dto, loadType);
        //封装返回
        ResponseResult result = ResponseResult.okResult(articleList);
        result.setHost(webSite);
        return result;
    }

    /**
     * 重新计算文章分值
     *
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        log.info("updateApArticle is begin: {}", mess);
        //1 查询文档
        ApArticle apArticle = getById(mess.getArticleId());
        if (apArticle == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //2计算文章分值
        Integer score = computeScore(apArticle);
        //当天的热点数据要*3
        score = score * 3;

        //3 修改文章数据
        //阅读
        if (mess.getView() != 0) {
            int view = (int) (apArticle.getViews() == null ? mess.getView() : mess.getView() + apArticle.getViews());
            apArticle.setViews(view);
        }
        if (mess.getLike() != 0) {
            int like = (int) (apArticle.getLikes() == null ? mess.getLike() : mess.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        if (mess.getComment() != 0) {
            int comment = (int) (apArticle.getComment() == null ? mess.getComment() : mess.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        if (mess.getCollect() != 0) {
            int collection = (int) (apArticle.getCollection() == null ? mess.getCollect() : mess.getCollect() + apArticle.getCollection());
            apArticle.setCollection(collection);
        }
        updateById(apArticle);

        //4 更新缓存频道
        String articleListStr = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        updateArticleCache(apArticle, score, articleListStr, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());

        //5 更新推荐列表缓存
        String articleDefaultStr = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);
        updateArticleCache(apArticle, score, articleDefaultStr, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        log.info("updateApArticle is success");
    }

    /**
     * 根据参数加载文章列表  v2
     *
     * @param loadtypeLoadMore
     * @param dto
     * @param firstPage
     * @return
     */
    @Override
    public ResponseResult load2(Short loadtypeLoadMore, ArticleHomeDto dto, boolean firstPage) {
        if (firstPage) {
            String jsonStr = redisTemplate.opsForValue().get(ArticleConstants.HOT_ARTICLE_FIRST_PAGE + dto.getTag());
            if (StringUtils.isNotBlank(jsonStr)) {
                List<HotArticleVo> hotArticleVoList = JSON.parseArray(jsonStr, HotArticleVo.class);
                if (!hotArticleVoList.isEmpty() && hotArticleVoList.size() > 0) {
                    ResponseResult result = ResponseResult.okResult(hotArticleVoList);
                    result.setHost(webSite);
                    return result;
                }
            }
        }
        return load(loadtypeLoadMore, dto);
    }


    /**
     * 更新文章缓存
     *
     * @param apArticle      当前文章
     * @param score          分数
     * @param articleListStr 缓存文章列表
     * @param tag
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String articleListStr, String tag) {
        log.info("updateApArticle updateArticleCache apArticle:{},score:{}", apArticle, score);
        boolean flag = false;
        if (StringUtils.isNotBlank(articleListStr)) {
            List<HotArticleVo> hotArticleVos = JSON.parseArray(articleListStr, HotArticleVo.class);
            //如果缓存中有文章,更新分值
            for (HotArticleVo hotArticleVo : hotArticleVos) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            //如果缓存中没有文章
            if (!flag) {
                // 如果文章列表文章数据大于等于30条记录，获取缓存中的最小分值的当前文章作对比
                if (hotArticleVos.size() >= 30) {
                    //排序
                    hotArticleVos = hotArticleVos.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                            .collect(Collectors.toList());
                    HotArticleVo hotArticleVo = hotArticleVos.get(hotArticleVos.size() - 1);
                    //缓存频道下的最后一条文章的分值小于当前文章分值 则替换
                    if (hotArticleVo.getScore() < score) {
                        //替换=先删除再添加
                        hotArticleVos.remove(hotArticleVo);

                        HotArticleVo hotArticle = new HotArticleVo();
                        BeanUtils.copyProperties(apArticle, hotArticle);
                        hotArticle.setScore(score);

                        hotArticleVos.add(hotArticle);
                    }
                } else {
                    //热点文章不足30条,直接添加到热点文章列表中
                    HotArticleVo hotArticle = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle, hotArticle);
                    hotArticle.setScore(score);
                    hotArticleVos.add(hotArticle);
                }
                // 更新到缓存
                // ****排序
                hotArticleVos = hotArticleVos.stream()
                        .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                        .limit(30)
                        .collect(Collectors.toList());

                redisTemplate.opsForValue().set(tag,
                        JSON.toJSONString(hotArticleVos));

                log.info("updateApArticle updateArticleCache success");
            }
        }
    }

    /**
     * 计算文章分值
     *
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        int score = 0;
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        return score;
    }

}
